#include <modules\graphics\sprite.h>

namespace DelGE
{
	namespace Modules
	{
		namespace Graphics
		{
			Sprite::Sprite()
			{
				this->pMesh = NULL;
				this->pTexture = NULL;

				this->iFrameWidth = 0;
				this->iFrameHeight = 0;

				this->oHandlePosition = Vector3();
				this->oPosition = Vector3();
				this->oScale = Vector3(1, 1, 1);
				this->fRotation = 0;

				this->eBlendMode = DelGEGraphicsBlendMode_Alpha;

				//this->pCurrentAnimation = NULL;
				this->pParent = NULL;
			}

			Sprite::Sprite(Texture* pTexture, int iFrameWidth, int iFrameHeight)
			{
				this->pTexture = pTexture;

				if (iFrameWidth == -1) iFrameWidth = pTexture->GetWidth();
				if (iFrameHeight == -1) iFrameHeight = pTexture->GetHeight();

				this->iFrameWidth = iFrameWidth;
				this->iFrameHeight = iFrameHeight;

				this->oHandlePosition = Vector3();
				this->oPosition = Vector3();
				this->oScale = Vector3(1, 1, 1);
				this->fRotation = 0;

				//this->pCurrentAnimation = NULL;

				this->pMesh = new Mesh();

				this->pMesh->AddVertex(0,					0,					0,	this->GetFrameStartU(0),	this->GetFrameStartV(0));
				this->pMesh->AddVertex((float)iFrameWidth,	0,					0,	this->GetFrameEndU(0),		this->GetFrameStartV(0));
				this->pMesh->AddVertex(0,					(float)iFrameHeight,0,	this->GetFrameStartU(0),	this->GetFrameEndV(0));
				this->pMesh->AddVertex((float)iFrameWidth,	(float)iFrameHeight,0,	this->GetFrameEndU(0),		this->GetFrameEndV(0));

				this->pMesh->AddIndexedTriangle(0, 1, 2);
				this->pMesh->AddIndexedTriangle(1, 3, 2);

				this->eBlendMode = DelGEGraphicsBlendMode_Alpha;

				this->pMesh->BuildMesh();
				this->pParent = NULL;
			}

			Sprite::~Sprite()
			{
				if (this->pMesh != NULL) delete this->pMesh; this->pMesh = NULL;
				this->pTexture = NULL;

				this->iFrameWidth = 0;
				this->iFrameHeight = 0;

				this->fRotation = 0;

				/*this->pCurrentAnimation = NULL;
				if (!this->mAnimaitonMap.empty())
				{
					map<string, Animation*>::iterator itor;

					for (itor = this->mAnimaitonMap.begin(); itor != this->mAnimaitonMap.end(); ++itor)
						delete (Animation *)itor->second;

					this->mAnimaitonMap.clear();
				}*/

				this->eBlendMode = DelGEGraphicsBlendMode_Alpha;

				if (this->pParent != NULL)
					this->pParent->RemoveChild(this);

				if (!this->vChildVector.empty())
				{
					for (unsigned int i = 0; i < this->vChildVector.size(); i ++)
						this->vChildVector[i]->SetParent(NULL);
				}

				this->pParent = NULL;
			}

			Mesh* Sprite::GetMesh()
			{
				return this->pMesh;
			}

			void Sprite::SetTexture(Texture* pTexture)
			{
				this->pTexture = pTexture;
			}

			Texture* Sprite::GetTexture()
			{
				return this->pTexture;
			}

			/*void Sprite::AddAnimation(string sName, int iStartFrame, int iEndFrame, float fFPS)
			{
				Animation* pAnimation;
				if (this->mAnimaitonMap.find(sName) == this->mAnimaitonMap.end())
				{
					pAnimation = new Animation(sName, iStartFrame, iEndFrame, fFPS);
					this->mAnimaitonMap[sName] = pAnimation;
				}
			}

			void Sprite::RemoveAnimation(string sName)
			{
				map<string, Animation*>::iterator itor;

				itor = this->mAnimaitonMap.find(sName);
				if (itor != this->mAnimaitonMap.end())
				{
					delete this->mAnimaitonMap[sName];
					this->mAnimaitonMap.erase(itor);
				}
			}

			Animation* Sprite::GetAnimation(string sName)
			{
				return this->mAnimaitonMap[sName];
			}

			Animation* Sprite::GetCurrentAnimation()
			{
				return this->pCurrentAnimation;
			}

			void Sprite::PlayAnimation(string sName, DelGEGraphics_Animation_Playstyle ePlaystyle)
			{
				if (this->mAnimaitonMap.find(sName) != this->mAnimaitonMap.end())
				{
					this->pCurrentAnimation = this->mAnimaitonMap[sName];
					this->pCurrentAnimation->SetPlaystyle(ePlaystyle);
					this->pCurrentAnimation->Start();
				}
			}

			void Sprite::StopAnimation()
			{
				if (this->pCurrentAnimation != NULL)
				{
					this->pCurrentAnimation->Stop();
					this->pCurrentAnimation = NULL;
				}
			}

			void Sprite::PauseAnimation()
			{
				if (this->pCurrentAnimation != NULL)
					this->pCurrentAnimation->Pause();
			}

			void Sprite::ResumeAnimation()
			{
				if (this->pCurrentAnimation != NULL)
					this->pCurrentAnimation->Resume();
			}

			bool Sprite::IsAnimationPlaying(string sName)
			{
				if (this->pCurrentAnimation != NULL)
					return (this->pCurrentAnimation->GetName() == sName);

				return false;
			}*/

			float Sprite::GetFrameStartU(int iFrame)
			{
				float fu = 0.0f;
				float fx = 0;
				fx = (float)(iFrame % GetFrameCountX());
				fx = (fx * (float)this->iFrameWidth) + 0.5f;
				fu = ((float)fx) / ((float)this->pTexture->GetWidthPow2());
				return  fu;
			}

			float Sprite::GetFrameStartV(int iFrame)
			{
				float fv = 0.0f;
				float fy = 0;
				fy = (float)(iFrame / GetFrameCountX());
				fy = (fy * (float)this->iFrameHeight) + 0.5f;
				fv = ((float)fy) / ((float)this->pTexture->GetHeightPow2());
				return  fv;
			}

			float Sprite::GetFrameEndU(int iFrame)
			{
				float fu = 0.0f;
				float fx = 0;
				fx = (float)(iFrame % GetFrameCountX());
				fx = ((fx + 1) * (float)this->iFrameWidth) - 0.5f;
				fu = ((float)fx) / ((float)this->pTexture->GetWidthPow2());
				return  fu;
			}

			float Sprite::GetFrameEndV(int iFrame)
			{
				float fv = 0.0f;
				float fy = 0;
				fy = (float)(iFrame / GetFrameCountX());
				fy = ((fy + 1) * (float)this->iFrameHeight) - 0.5f;
				fv = ((float)fy) / ((float)this->pTexture->GetHeightPow2());
				return  fv;
			}

			int Sprite::GetFrameCountX()
			{
				if (this->iFrameWidth == 0) return 0;
				return this->pTexture->GetWidth() / this->iFrameWidth;
			}

			int Sprite::GetFrameCountY()
			{
				if (this->iFrameHeight == 0) return 0;
				return this->pTexture->GetHeight() / this->iFrameHeight;
			}

			int Sprite::GetFrameCount()
			{
				return this->GetFrameCountX() * this->GetFrameCountY();
			}

			int Sprite::GetFrameWidth()
			{
				return this->iFrameWidth;
			}

			int Sprite::GetFrameHeight()
			{
				return this->iFrameHeight;
			}

			void Sprite::SetHandlePosition(float fX, float fY)
			{
				this->oHandlePosition = Vector3(fX, fY, 0);
			}

			void Sprite::SetHandlePosition(const Vector3& rVector)
			{
				this->oHandlePosition = rVector;
			}

			void Sprite::SetHandlePositionX(float fX)
			{
				this->oHandlePosition[0] = fX;
			}

			void Sprite::SetHandlePositionY(float fY)
			{
				this->oHandlePosition[1] = fY;
			}

			Vector3 Sprite::GetHandlePosition()
			{
				return this->oHandlePosition;
			}

			float Sprite::GetHandlePositionX()
			{
				return this->GetHandlePosition()[0];
			}

			float Sprite::GetHandlePositionY()
			{
				return this->GetHandlePosition()[1];
			}

			void Sprite::SetPosition(float fX, float fY)
			{
				this->oPosition = Vector3(fX, fY);
			}

			void Sprite::SetPosition(const Vector3& rVector)
			{
				this->oPosition = rVector;
			}

			void Sprite::SetPositionX(float fX)
			{
				this->oPosition[0] = fX;
			}

			void Sprite::SetPositionY(float fY)
			{
				this->oPosition[1] = fY;
			}

			Vector3 Sprite::GetPosition(bool bGlobal)
			{
				if (bGlobal)
				{
					Matrix globalMatrix;
					if (this->pParent != NULL)
						globalMatrix = this->pParent->GetGlobalMatrix();
					return this->oPosition * globalMatrix;
				}
				else
					return this->oPosition;
			}

			float Sprite::GetPositionX(bool bGlobal)
			{
				return this->GetPosition(bGlobal)[0];
			}

			float Sprite::GetPositionY(bool bGlobal)
			{
				return this->GetPosition(bGlobal)[1];
			}

			void Sprite::SetScale(float fScaleX, float fScaleY)
			{
				this->oScale = Vector3(fScaleX, fScaleY);
			}

			void Sprite::SetScale(const Vector3& rVector)
			{
				this->oScale = rVector;
			}
			void Sprite::SetScaleX(float fScaleX)
			{
				this->oScale[0] = fScaleX;
			}

			void Sprite::SetScaleY(float fScaleY)
			{
				this->oScale[1] = fScaleY;
			}

			Vector3 Sprite::GetScale(bool bGlobal)
			{
				if (bGlobal && (this->pParent != NULL))
					return this->oScale * this->pParent->GetScale();

				return this->oScale;
			}

			float Sprite::GetScaleX(bool bGlobal)
			{
				return this->GetScale(bGlobal)[0];
			}

			float Sprite::GetScaleY(bool bGlobal)
			{
				return this->GetScale(bGlobal)[1];
			}

			void Sprite::SetRotation(float fRotation)
			{
				this->fRotation = fRotation;
			}

			float Sprite::GetRotation(bool bGlobal)
			{
				if (bGlobal)
				{
					if (this->pParent != NULL)
						return this->fRotation + this->pParent->GetRotation();
				}

				return this->fRotation;
			}

			int Sprite::GetWidth()
			{
				return (int)(this->iFrameWidth * this->oScale[0]);
			}

			int Sprite::GetHeight()
			{
				return (int)(this->iFrameHeight * this->oScale[1]);
			}

			void Sprite::SetColor(int iRed, int iGreen, int iBlue, int iAlpha)
			{
				this->oColor = Color(iRed, iGreen, iBlue, iAlpha);

				for (int i = 0; i < this->pMesh->GetVertexCount(); ++i)
					this->pMesh->GetVertex(i)->color = this->oColor;
			}

			void Sprite::SetColor(const Color& rColor)
			{
				this->oColor = rColor;

				for (int i = 0; i < this->pMesh->GetVertexCount(); ++i)
					this->pMesh->GetVertex(i)->color = this->oColor;
			}

			Color Sprite::GetColor()
			{
				return this->oColor;
			}

			void Sprite::SetBlendMode(DelGEGraphicsBlendMode eBlendMode)
			{
				this->eBlendMode = eBlendMode;
			}

			DelGEGraphicsBlendMode Sprite::GetBlendMode()
			{
				return this->eBlendMode;
			}

			void Sprite::SetParent(Sprite* pSprite)
			{
				if (this->pParent != NULL)
					this->pParent->RemoveChild(this);

				this->pParent = pSprite;
				if (this->pParent != NULL)
					this->pParent->AddChild(this);
			}

			void Sprite::AddChild(Sprite* pSprite)
			{
				this->vChildVector.push_back(pSprite);
			}

			void Sprite::RemoveChild(Sprite* pSprite)
			{
				unsigned int i;
				Sprite *pFoundSprite = NULL;

				for (i = 0; i < this->vChildVector.size(); i ++)
				{
					if (this->vChildVector[i] == pSprite)
					{
						pFoundSprite = this->vChildVector[i];
						break;
					}
				}

				if (pFoundSprite != NULL) this->vChildVector.erase(this->vChildVector.begin() + i);
			}

			Sprite *Sprite::GetParent()
			{
				return this->pParent;
			}

			vector<Sprite*> Sprite::GetChildren()
			{
				return this->vChildVector;
			}

			void Sprite::UpdateAnimation(float fDelta)
			{
				Animatable::UpdateAnimation(fDelta);
			}

			void Sprite::Update(float fDelta)
			{
				int iFrame = 0;

				if (this->pCurrentAnimation != NULL)
				{	
					UpdateAnimation(fDelta);
					iFrame = this->pCurrentAnimation->GetCurrentFrame();				
				}

				this->pMesh->GetVertex(0)->position[0] = -this->iFrameWidth * this->oHandlePosition[0]; this->pMesh->GetVertex(0)->position[1] = -this->iFrameHeight * this->oHandlePosition[1];
				this->pMesh->GetVertex(1)->position[0] = (-this->iFrameWidth * this->oHandlePosition[0]) + this->iFrameWidth; this->pMesh->GetVertex(1)->position[1] = -this->iFrameHeight * this->oHandlePosition[1];
				this->pMesh->GetVertex(2)->position[0] = -this->iFrameWidth * this->oHandlePosition[0]; this->pMesh->GetVertex(2)->position[1] = (-this->iFrameHeight * this->oHandlePosition[1]) + this->iFrameHeight;
				this->pMesh->GetVertex(3)->position[0] = (-this->iFrameWidth * this->oHandlePosition[0]) + this->iFrameWidth; this->pMesh->GetVertex(3)->position[1] = (-this->iFrameHeight * this->oHandlePosition[1]) + this->iFrameHeight;

				this->pMesh->GetVertex(0)->textureUV[0] = this->GetFrameStartU(iFrame);	this->pMesh->GetVertex(0)->textureUV[1] = this->GetFrameStartV(iFrame);
				this->pMesh->GetVertex(1)->textureUV[0] = this->GetFrameEndU(iFrame);	this->pMesh->GetVertex(1)->textureUV[1] = this->GetFrameStartV(iFrame);
				this->pMesh->GetVertex(2)->textureUV[0] = this->GetFrameStartU(iFrame);	this->pMesh->GetVertex(2)->textureUV[1] = this->GetFrameEndV(iFrame);
				this->pMesh->GetVertex(3)->textureUV[0] = this->GetFrameEndU(iFrame);	this->pMesh->GetVertex(3)->textureUV[1] = this->GetFrameEndV(iFrame);

				this->pMesh->BuildMesh();
			}

			Matrix Sprite::GetLocalMatrix()
			{
				Matrix localMatrix;

				localMatrix = Matrix::Create2DTranslation(this->GetPosition(false)[0], this->GetPosition(false)[1]) *
							  Matrix::Create2DRotation(this->GetRotation(false)) *
							  Matrix::Create2DScale(this->GetScale(false)[0], this->GetScale(false)[1]);

				return localMatrix;
			}

			Matrix Sprite::GetGlobalMatrix()
			{
				Matrix globalMatrix = this->GetLocalMatrix();

				if (this->pParent != NULL)
					globalMatrix = globalMatrix * this->pParent->GetGlobalMatrix();

				return globalMatrix;
			}
		}
	}
}
